{
 "cells": [
  {
   "cell_type": "code",
   "execution_count": 50,
   "id": "c710cbfb",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "('消化能（Mcal/kg）',\n",
       " '粗蛋白（%）',\n",
       " '精氨酸（%）',\n",
       " '组氨酸（%）',\n",
       " '亮氨酸（%）',\n",
       " '异亮氨酸（%）',\n",
       " '蛋氨酸（%）',\n",
       " '赖氨酸（%）',\n",
       " '苏氨酸（%）',\n",
       " '色氨酸（%）',\n",
       " '苯丙氨酸（%）',\n",
       " '缬氨酸（%）',\n",
       " '钙（%）',\n",
       " '总磷（%）')"
      ]
     },
     "execution_count": 50,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "from scipy.optimize import minimize\n",
    "import openpyxl\n",
    "from collections import OrderedDict\n",
    "\n",
    "\n",
    "workbook = openpyxl.load_workbook('data/算法大作业数据.xlsx')\n",
    "\n",
    "\"\"\"----------------------饲料原料及其营养成分含量----------------------\"\"\"\n",
    "worksheet = workbook['饲料原料及其营养成分含量']\n",
    "rows_1 = []\n",
    "factors = []\n",
    "for row in worksheet.iter_rows(values_only=True): \n",
    "    # if \"%\" in row[0]:\n",
    "    rows_1.append(row)\n",
    "cols_1 = zip(*rows_1)\n",
    "feed_data = {col[0]: col[1:15] for col in cols_1}\n",
    "feed_data.pop('原料名称')"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 31,
   "id": "644809ba",
   "metadata": {
    "scrolled": true
   },
   "outputs": [
    {
     "data": {
      "text/plain": [
       "['20-50kg', '50-80kg', '80-120kg']"
      ]
     },
     "execution_count": 31,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "\"\"\"----------------------生猪饲养标准----------------------\"\"\"\n",
    "worksheet = workbook['生猪饲养标准']\n",
    "\n",
    "rows_2 = []\n",
    "for row in worksheet.iter_rows(values_only=True): rows_2.append(row)\n",
    "cols_2 = zip(*rows_2)\n",
    "feeding_standards = {col[0]:col[1:] for col in cols_2 }\n",
    "stages = [_ for _ in feeding_standards.keys() if _ and \"kg\" in _]\n",
    "stages\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 53,
   "id": "ed0f3d42",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "{'玉米': 90,\n",
       " '麦麸': 20,\n",
       " '豆粕': 60,\n",
       " '鱼粉': 5,\n",
       " '棉籽油': 9,\n",
       " '菜籽油': 7,\n",
       " '植物油': 3,\n",
       " '石粉': 2,\n",
       " '磷酸氢钙': 10,\n",
       " '蛋氨酸': 3,\n",
       " '赖氨酸': 3,\n",
       " '复合预混料': 0,\n",
       " '食盐': 0}"
      ]
     },
     "execution_count": 53,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "upper_limits = {key:vlaue for key, vlaue in zip(rows_1[0][1:],rows_1[15][1:])}\n",
    "# upper_limits = rows_1[15][1:]\n",
    "lower_limits = {key:vlaue for key, vlaue in zip(rows_1[0][1:],rows_1[16][1:])}\n",
    "# lower_limits = rows_1[16][1:]\n",
    "upper_limits"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 34,
   "id": "b0c3f0db",
   "metadata": {
    "scrolled": true
   },
   "outputs": [
    {
     "data": {
      "text/plain": [
       "(1.5, 1.5, 2.8, 6, 1.6, 1.4, 6, 0.2, 1.35, 28, 22, 10, 0.86)"
      ]
     },
     "execution_count": 34,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "prices = rows_1[18][1:]\n",
    "prices"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 35,
   "id": "2a2af80f",
   "metadata": {},
   "outputs": [],
   "source": [
    "def objective(x):\n",
    "    total_price = sum([price * i for i, price in zip(x, prices)])\n",
    "    return total_price"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 48,
   "id": "74891f81",
   "metadata": {},
   "outputs": [],
   "source": [
    "def constraint_generator(factors, objective_criteria:float, type='ineq', reverse=False):\n",
    "    def result(x):\n",
    "        total_content = sum([ x * y for x,y in zip(x, factors)])\n",
    "#         if reverse: return objective_criteria - total_content\n",
    "        return total_content - objective_criteria\n",
    "#     return {'type': type, 'fun': result}\n",
    "    return result"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 37,
   "id": "0fce8011",
   "metadata": {},
   "outputs": [],
   "source": [
    "def nutritional_constraints_generator(stage):\n",
    "    '''营养物限制生成器'''\n",
    "    constraints = []\n",
    "    for objective_criteria, i in zip(feeding_standards[stage], range(1, 15)):\n",
    "        factors = rows_1[i][1:]\n",
    "        constraints.append(constraint_generator(factors, objective_criteria))\n",
    "    return constraints"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 40,
   "id": "2efa8052",
   "metadata": {},
   "outputs": [],
   "source": [
    "def ratio_constraints_generator()->list:\n",
    "    '''比例限制生成器'''\n",
    "    constraints = []\n",
    "    for i, feed_name in enumerate(feed_names):\n",
    "        constraints.append(lambda x : x[i] - upper_limits[feed_name])\n",
    "        constraints.append(lambda x : lower_limits[feed_name] - x[i])\n",
    "    return constraints"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 81,
   "id": "c3ec3e29",
   "metadata": {},
   "outputs": [],
   "source": [
    "def x_constraint(x):\n",
    "    for i in x: \n",
    "        if i<0: return -1 \n",
    "    return sum(x) - 1.0\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 61,
   "id": "dd3a83ee",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "[0.7576672939323541, 0.015984153775176205, 0.029806539290636015, 0.09576109396187651, 0.10078091903995717]\n",
      "Sum: 1.0\n"
     ]
    }
   ],
   "source": [
    "import random\n",
    "\n",
    "def generate_numbers(n):\n",
    "    numbers = []\n",
    "    remaining_sum = 1.0\n",
    "    for _ in range(n-1):\n",
    "        # 生成 0 到 remaining_sum 之间的随机数\n",
    "        number = random.uniform(0, remaining_sum)\n",
    "        # 将随机数添加到列表中\n",
    "        numbers.append(number)\n",
    "        # 更新剩余总和\n",
    "        remaining_sum -= number\n",
    "\n",
    "    # 最后一个数即为剩余总和，确保加起来等于 1\n",
    "    numbers.append(remaining_sum)\n",
    "\n",
    "    return numbers\n",
    "\n",
    "# 生成一个长度为 5 的数字列表\n",
    "numbers = generate_numbers(5)\n",
    "print(numbers)\n",
    "print(\"Sum:\", sum(numbers))  # 验证加起来是否为 1\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "ddda978b",
   "metadata": {},
   "outputs": [],
   "source": []
  },
  {
   "cell_type": "code",
   "execution_count": 94,
   "id": "a9383210",
   "metadata": {
    "scrolled": false
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "=================stage: 20-50kg===============\n",
      "---------------round 0----------------\n",
      "目标函数值（最小价格）: 1.3626112918720463\n",
      "最优饲料配方:\n",
      "玉米: 0.544710501217479\n",
      "麦麸: 3.3897277346151e-13\n",
      "豆粕: 3.5040720325341113e-13\n",
      "鱼粉: 3.0644757564868286e-14\n",
      "棉籽油: 5.062478214412636e-13\n",
      "菜籽油: 0.37873970023755443\n",
      "植物油: 4.270889197854899e-14\n",
      "石粉: 0.07654979854669551\n",
      "磷酸氢钙: 5.133627897779824e-13\n",
      "蛋氨酸: 0.0\n",
      "赖氨酸: 0.0\n",
      "复合预混料: 0.0\n",
      "食盐: 5.578367628933378e-13\n",
      "=================stage: 50-80kg===============\n",
      "---------------round 0----------------\n",
      "目标函数值（最小价格）: 1.3585190503870497\n",
      "最优饲料配方:\n",
      "玉米: 0.6398274612758952\n",
      "麦麸: 2.295628964699148e-13\n",
      "豆粕: 4.159086269828194e-14\n",
      "鱼粉: 5.091413401991929e-16\n",
      "棉籽油: 0.0\n",
      "菜籽油: 0.2722861255725159\n",
      "植物油: 0.0\n",
      "石粉: 0.08788641315035661\n",
      "磷酸氢钙: 0.0\n",
      "蛋氨酸: 9.085995844593242e-13\n",
      "赖氨酸: 6.501882365839151e-13\n",
      "复合预混料: 1.4043453899770242e-13\n",
      "食盐: 0.0\n",
      "=================stage: 80-120kg===============\n",
      "---------------round 0----------------\n",
      "目标函数值（最小价格）: 1.35544986939864\n",
      "最优饲料配方:\n",
      "玉米: 0.7111651824251218\n",
      "麦麸: 3.637680434653845e-13\n",
      "豆粕: 2.3536207705010526e-13\n",
      "鱼粉: 6.2727600891321345e-15\n",
      "棉籽油: 3.057328695765804e-13\n",
      "菜籽油: 0.19244594353595965\n",
      "植物油: 2.309610835915521e-14\n",
      "石粉: 0.09638887404008888\n",
      "磷酸氢钙: 3.18028589574304e-13\n",
      "蛋氨酸: 0.0\n",
      "赖氨酸: 0.0\n",
      "复合预混料: 0.0\n",
      "食盐: 3.4516313418553324e-13\n"
     ]
    }
   ],
   "source": [
    "\n",
    "feed_names = list(feed_data.keys())\n",
    "bounds = [(0, 1) for _ in range(len(feed_names))]\n",
    "\n",
    "for stage in stages:\n",
    "    min_price = 2.0\n",
    "    min_solution = None\n",
    "    print(\"=================stage: %s===============\"%stage)\n",
    "    for i in range(5):\n",
    "        if i %100 == 0: print(\"---------------round %d----------------\"%i)\n",
    "        initial_guess = generate_numbers(len(feed_names))\n",
    "    #     constraints = [{'type': 'eq', 'fun': f} for f in nutritional_constraints_generator(stage)]+ \n",
    "    #                    [{'type': 'ineq', 'fun': f} for f in ratio_constraints_generator}]\n",
    "        constraints = [{'type': 'ineq', 'fun': f} for f in nutritional_constraints_generator(stage)]\\\n",
    "                    + [{'type': 'ineq', 'fun': f} for f in ratio_constraints_generator()]\\\n",
    "                    +[{'type': 'eq', 'fun': x_constraint}]\n",
    "#         print(constraints)\n",
    "        solution = minimize(objective, initial_guess, bounds=bounds,  constraints=constraints, method='SLSQP')\n",
    "\n",
    "        if initial_guess[0] == solution.x[0] : pass\n",
    "        else:\n",
    "            if solution.fun < min_price: \n",
    "                min_price = solution.fun\n",
    "                min_solution = solution\n",
    "    print(f\"目标函数值（最小价格）: {min_solution.fun}\")\n",
    "    print(\"最优饲料配方:\")\n",
    "    for i, feed_name in enumerate(feed_names):\n",
    "        print(f\"{feed_name}: {min_solution.x[i]}\")\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "88130158",
   "metadata": {},
   "outputs": [],
   "source": []
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "4f46709f",
   "metadata": {},
   "outputs": [],
   "source": []
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.6.13"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 5
}
